<?php
/**
 * @File:    Pay.php
 * @Author:  LiaoJiangYi
 * @Website: www.jyliao.com
 * @Wechat:  jyliao_vip
 * @QQ:      527532113
 * @Date:    2016/1/19 18:14
 */
namespace Ext\WeChat;

use Ext\Utils;

class Pay extends Base
{
    private $appId;
    private $secret;
    private $key;
    private $sslCert;
    private $sslKey;
    private $nonceStr;
    private $mch_id;
    private $notify_url;
    const API_URL_PREFIX = 'https://api.mch.weixin.qq.com';
    const SEND_RED_PACK = '/mmpaymkttransfers/sendredpack';
    const PAY_UNIFIED_ORDER = '/pay/unifiedorder';
    const PAY_CLOSE_ORDER = '/pay/closeorder';

    public function __construct(\Phalcon\Config $options)
    {
        $this->appId = $options->appId;
        $this->secret = $options->secret;
        $this->key = $options->key;
        $this->sslCert = $options->sslCert;
        $this->sslKey = $options->sslKey;
        $this->nonceStr = $options->nonceStr;
        $this->mch_id = $options->mch_id;
        $this->notify_url = $options->notify_url;
    }

    public function requestAccessToken()
    {

    }

    //发送现金红包
    public function sendRedPack(array $options)
    {
        $this->createSign($options);
        return $this->_post(self::API_URL_PREFIX . self::SEND_RED_PACK, $options);
    }

    //创建支付订单
    public function payUnifiedOrder(array $options)
    {
        $this->createSign($options, 'payUnifiedOrder');
        return $this->_post(self::API_URL_PREFIX . self::PAY_UNIFIED_ORDER, $options);
    }

    //取消订单
    public function payCloseOrder(array $options)
    {
        $this->createSign($options, 'payCloseOrder');
        return $this->_post(self::API_URL_PREFIX . self::PAY_CLOSE_ORDER, $options);
    }

    /*
     * 创建签名
     * @param $options array
     * @param $type string {sendPack:现金红包,userH5Pay:H5页面调用支付,payUnifiedOrder:下单}
     * */
    public function createSign(array &$options, $type = 'sendPack')
    {
        if ($type == 'sendPack') {
            $options['mch_id'] = $this->mch_id;
            $options['nonce_str'] = $this->getNonceStr();
            $options['wxappid'] = $this->appId;
        } elseif ($type == 'payUnifiedOrder') {
            $options['mch_id'] = $this->mch_id;
            $options['nonce_str'] = $this->getNonceStr();
            $options['appid'] = $this->appId;
            if (!isset($options['fee_type'])) $options['fee_type'] = 'CNY';
            if (!isset($options['sign_type'])) $options['sign_type'] = 'MD5';
            if (!isset($options['spbill_create_ip'])) $options['spbill_create_ip'] = Utils::getIp();
            if (!isset($options['timeStamp'])) $options['timeStamp'] = time();
            if (!isset($options['time_start'])) $options['time_start'] = date('YmdHis');
            if (!isset($options['time_expire'])) $options['time_expire'] = date('YmdHis', time() + 86400 * 2);
            if (!isset($options['notify_url'])) $options['notify_url'] = $this->notify_url;
            if (!isset($options['trade_type'])) $options['trade_type'] = 'JSAPI';
        } elseif ($type == 'userH5Pay') {
            $options['signType'] = 'MD5';
            $options['appId'] = $this->appId;
            $options['nonceStr'] = $this->getNonceStr();
            $options['timeStamp'] = (string)time();
        } elseif ($type == 'payCloseOrder') {
            $options['appid'] = $this->appId;
            $options['mch_id'] = $this->mch_id;
            $options['sign_type'] = 'MD5';
            $options['nonce_str'] = $this->getNonceStr();
        }
        ksort($options);
        $string = $this->toUrlParams($options);
        $string = $string . "&key=" . $this->key;
        if ($type == 'userH5Pay') {
            $options['paySign'] = strtoupper(md5($string));
        } else $options['sign'] = strtoupper(md5($string));
        return true;
    }

    /*
     * 支付成功验证签名
     * */
    public function paySuccessSign(array $options)
    {
        $sign = $options['sign'];
        unset($options['sign']);
        $string = $this->toUrlParams($options);
        $string = $string . "&key=" . $this->key;
        if (strtoupper(md5($string)) == $sign) return true;
        else return false;
    }

    private function toUrlParams(array $options)
    {
        $buff = "";
        foreach ($options as $k => $v) {
            if ($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
        }
        $buff = trim($buff, "&");
        return $buff;
    }

    public function getNonceStr()
    {
        return strtoupper(md5($this->nonceStr . time()));
    }

    protected function requestJsApiTicket()
    {
    }

    public function jsApiConfig(array $config = [])
    {
    }

    protected function createMessageCrypt()
    {
    }

    protected function getCacheKey($name)
    {
        return $name;
    }

    public function parseHttpRequest(callable $callable, $url, $postOptions = null)
    {
        return $callable($url, $postOptions);
    }

    private function _post($url, array $options, $second = 30, array $aHeader = [])
    {
        //echo self::toXml($options);exit;
        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        //如果有配置代理这里就设置代理
        /*if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0"
            && WxPayConfig::CURL_PROXY_PORT != 0){
            curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST);
            curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT);
        }*/
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);//严格校验
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        //设置证书
        //使用证书：cert 与 key 分别属于两个.pem文件
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLCERT, $this->sslCert);
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLKEY, $this->sslKey);

        if (count($aHeader) >= 1) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $aHeader);
        }
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, self::toXml($options));
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if ($data) {
            curl_close($ch);
            return self::toArray($data);
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            return $error;
        }
    }

    /**
     * The main function for converting to an XML document.
     * Pass in a multi dimensional array and this recrusively loops through and builds up an XML document.
     *
     * @param array $data
     * @param string $rootNodeName - what you want the root node to be - defaultsto data.
     * @param SimpleXMLElement $xml - should only be used recursively
     * @return string XML
     */
    public static function toXml(array $data)
    {
        $xml = '<xml>';
        foreach ($data as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= '</xml>';
        return $xml;
    }

    public static function toArray($xml)
    {
        //将XML转为array
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }

    /**
     * 计算微信红包金额, 2位小数点
     * @param $remainSize int 剩余的红包数量
     * @param $remainMoney float 剩余的钱
     * @return double
     * */
    public function getRandomMoney($remainSize,$remainMoney)
    {
        if ($remainSize == 1)return $remainMoney;
        if(($remainMoney/$remainSize)==1)
        {
            return 1;
        }
        $remainMoney = $remainMoney - $remainSize;
        $min = 0;
        $max = $remainMoney / $remainSize * 2;
        $money = (mt_rand($min*100,100)/100) * $max;
        $money = $money <= $min ? 0 : $money;
        $money = floor($money * 100) / 100;
        return floatval($money+1);
    }
}